
#include "OpenGL.h"
#include "Camera.h"
#include "Mouse.h"
// #include "Displacement.h"
// #include "SubdivisionSurface.h"
// #include "Light.h"
// #include "MaterialManager.h"
// #include "Tga.h"
// #include "Rock.h"
// #include "RockManager.h"
#include "GraphicsUtility.h"
// #include "MeshRenderer.h"
// #include "Drawable.h"
// #include "TriangleMesh.h"

// #include "Vertex.h"
#include "Vector.h"
// #include "TriangleMesh.h"

using namespace Velyn;
using namespace OhioState::Math;
using namespace std;

//-------------------------------------------------------------------------------
// Prototypes
//-------------------------------------------------------------------------------

void display();
void keyboardPress(unsigned char key, int x, int y);
void keyboardRelease(unsigned char key, int x, int y);
void reshape(int w, int h);
void timer(int extra);
void initialize();
void mouseButtonPress(int button, int state, int x, int y);
void mouseMotion(int x, int y);

//-------------------------------------------------------------------------------
// Globals
//-------------------------------------------------------------------------------

Camera camera(4.0f);
Mouse mouse;

Vector3 midpoints[3];
Vector3 IPosition[3], INormal[3];
Vector3 f3Position[3], f3Normal[3];
Vector3 projection[3];
Vector3 triangleCenter;

//-------------------------------------------------------------------------------
// Display
//-------------------------------------------------------------------------------

void display() {
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	camera.enable();
    mouse.rotateObject();
    GraphicsUtility::compass();
    
    glPointSize(5.0f);
    
    glBegin(GL_POINTS);
    glColor3f(1.0f, 0.0f, 0.0f);
    glVertex3fv(IPosition[0].getValue());
    glVertex3fv(IPosition[1].getValue());
    glVertex3fv(IPosition[2].getValue());
    glEnd();
 
    glBegin(GL_POINTS);
    glColor3f(0.0f, 0.0f, 1.0f);
    glVertex3fv(triangleCenter.getValue());
    glEnd();
    
    glBegin(GL_POINTS);
    glColor3f(1.0f, 1.0f, 1.0f);
    glVertex3fv(f3Position[0].getValue());
    glVertex3fv(f3Position[1].getValue());
    glVertex3fv(f3Position[2].getValue());
    glEnd();
    
    glutSwapBuffers();
}

//-------------------------------------------------------------------------------
// Keyboard Press
//-------------------------------------------------------------------------------

void keyboardPress(unsigned char key, int x, int y) {
	switch(key) {
		case 27: {
			exit(0);
		} break;
			
		case '`': {
		} break;
			
		case 'a': {
        } break;
            
		case 'w': {	
		} break;
            
		default: {
		} break;
	}
}

//-------------------------------------------------------------------------------
// Keyboard Release
//-------------------------------------------------------------------------------

void keyboardRelease(unsigned char key, int x, int y) {}

//-------------------------------------------------------------------------------
// Reshape
//-------------------------------------------------------------------------------

void reshape(int w, int h) {
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(60.0, 1.6, 1.0, 200.0);
}

//-------------------------------------------------------------------------------
// Timer
//-------------------------------------------------------------------------------

void timer(int extra) {
	glutPostRedisplay();
	glutTimerFunc(40, timer, 0);
}

//-------------------------------------------------------------------------------
// Initialize
//-------------------------------------------------------------------------------

// Phong Tessellation
// http://www.gamedev.net/topic/565722-help-with-phong-tessellation/
// http://www.youtube.com/watch?v=9s0onJyTmWE
// http://http.developer.nvidia.com/GPUGems3/gpugems3_ch05.html
// http://developer.download.nvidia.com/presentations/2010/gdc/NVIDIA_Tessellation_GDC10.pdf
// http://onrendering.blogspot.com/2011/12/tessellation-on-gpu-curved-pn-triangles.htmlb
// http://perso.telecom-paristech.fr/~boubek/papers/PhongTessellation/PhongTessellation.pdf

// Barycentric Coordinates
// http://blogs.msdn.com/b/rezanour/archive/2011/08/07/barycentric-coordinates-and-point-in-triangle-tests.aspx
// http://crackthecode.us/barycentric/barycentric_coordinates.html
// http://www.farinhansford.com/dianne/teaching/cse470/materials/BarycentricCoords.pdf

// Calculate Barycentric Coordinates
// http://www.gamedev.net/topic/621445-barycentric-coordinates-c-code-check/

// write test cases to get this to work

void initialize() {
    
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	glShadeModel(GL_SMOOTH);
    
    IPosition[0].setValue(0.0f, 1.0f, 0.5f);
    IPosition[1].setValue(1.0f, -1.0f, 0.5f);
    IPosition[2].setValue(-1.0f, -1.0f, 0.5f);
 
    INormal[0].setValue(0.0f, 1.0f, 0.5f);
    INormal[1].setValue(1.0f, -1.0f, 0.5f);
    INormal[2].setValue(-1.0f, -1.0f, 0.5f);

    INormal[0].normalize();
    INormal[1].normalize();
    INormal[2].normalize();
    
    midpoints[0] = (IPosition[0] + IPosition[1]) * 0.5;
    midpoints[1] = (IPosition[1] + IPosition[2]) * 0.5;
    midpoints[2] = (IPosition[2] + IPosition[0]) * 0.5;
    
    cout << "Midpoint0: ";
    midpoints[0].print();
    
    cout << "Midpoint1: ";
    midpoints[1].print();
    
    cout << "Midpoint2: ";
    midpoints[2].print();
    
    triangleCenter = (midpoints[0] + midpoints[1] + midpoints[2]) * 0.3333f;
    
    cout << "Triangle Center: ";
    triangleCenter.print();

    float fU = 0.0f;
    float fV = 0.5f;
    float fW = 0.5f;
    
    f3Position[0] = (IPosition[0] * fW) + (IPosition[1] * fV) + (IPosition[2] * fU);
 
    projection[0] = f3Position[0] - (f3Position[0]-IPosition[0]).dot(INormal[0]) * INormal[0];
    projection[1] = f3Position[0] - (f3Position[0]-IPosition[1]).dot(INormal[1]) * INormal[1];
    projection[2] = f3Position[0] - (f3Position[0]-IPosition[2]).dot(INormal[2]) * INormal[2];
    
    f3Position[0] = (projection[0] * fW) + (projection[1] * fV) + (projection[2] * fU);
 
    f3Normal[0] = (INormal[0] * fW) + (INormal[1] * fV) + (INormal[2] * fU);
    
    fU = 0.5f;
    fV = 0.5f;
    fW = 0.0f;
    
    f3Position[1] = (IPosition[0] * fW) + (IPosition[1] * fV) + (IPosition[2] * fU);

    projection[0] = f3Position[1] - (f3Position[1]-IPosition[0]).dot(INormal[0]) * INormal[0];
    projection[1] = f3Position[1] - (f3Position[1]-IPosition[1]).dot(INormal[1]) * INormal[1];
    projection[2] = f3Position[1] - (f3Position[1]-IPosition[2]).dot(INormal[2]) * INormal[2];
    
    f3Position[1] = (projection[0] * fW) + (projection[1] * fV) + (projection[2] * fU);
    
    f3Normal[1] = (INormal[0] * fW) + (INormal[1] * fV) + (INormal[2] * fU);
    
    fU = 0.5f;
    fV = 0.0f;
    fW = 0.5f;
    
    f3Position[2] = (IPosition[0] * fW) + (IPosition[1] * fV) + (IPosition[2] * fU);
    
    projection[0] = f3Position[2] - (f3Position[2]-IPosition[0]).dot(INormal[0]) * INormal[0];
    projection[1] = f3Position[2] - (f3Position[2]-IPosition[1]).dot(INormal[1]) * INormal[1];
    projection[2] = f3Position[2] - (f3Position[2]-IPosition[2]).dot(INormal[2]) * INormal[2];

    f3Position[2] = (projection[0] * fW) + (projection[1] * fV) + (projection[2] * fU);
    
    f3Normal[2] = (INormal[0] * fW) + (INormal[1] * fV) + (INormal[2] * fU);
    
    cout << "f3Position[0]:";
    f3Position[0].print();
    
    cout << "f3Position[1]:";
    f3Position[1].print();
    
    cout << "f3Position[2]:";
    f3Position[2].print();
    
    glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
    
	glMatrixMode(GL_PROJECTION);		
	glLoadIdentity();
	gluPerspective(45.0, 1.6, 1.0, 200.0);
}

//-------------------------------------------------------------------------------
// Mouse Press
//-------------------------------------------------------------------------------

void mouseButtonPress(int button, int state, int x, int y) {
    mouse.pressEvent(button, state, x, y);
}

//-------------------------------------------------------------------------------
// Mouse Motion
//-------------------------------------------------------------------------------

void mouseMotion(int x, int y) {
    mouse.motionEvent(x, y);
}

//-------------------------------------------------------------------------------
// Main
//-------------------------------------------------------------------------------

int main(int argc, char** argv) {
	
    glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 500);
	glutInitWindowPosition(100, 100);
	glutCreateWindow("Smoothing Test");
	initialize();
	
	glutKeyboardFunc(keyboardPress);
	glutKeyboardUpFunc(keyboardRelease);
	
	glutMouseFunc(mouseButtonPress);
	glutMotionFunc(mouseMotion);
	
	glutTimerFunc(25, timer, 0);
	glutDisplayFunc(display);
	
	glutReshapeFunc(reshape);
    
	glutMainLoop();
}
